NJS.Control = Class.extend({
    INVALID: "INVALID",
    _throwReadOnlyError: function (property) {
        new Error(this.CLASS_SIGNATURE + ": Sorry [" + property + "] is a read-only property")
    },
    _assignEvents: function (group, target) {
        var key, handlers;
        var instance = this;
        this._activeEvents = this._activeEvents || {};
        if (typeof target === "string") {
            key = group + "/" + target;
            if (this._activeEvents[key]) {
                return
            }
            handlers = this._activeEvents[key] = {};
            $.each(this._events[group], function (eventType, handler) {
                handlers[eventType] = function (event) {
                    handler.call(instance, event, $(this))
                };
                $(document).delegate(target, eventType, handlers[eventType])
            })
        } else {
            target = $(target);
            if (target.length === 0) {
                return
            }
            if (this._activeEvents[group + "/" + target[0][$.expando]]) {
                return
            }
            handlers = {};
            $.each(this._events[group], function (eventType, handler) {
                handlers[eventType] = function (event) {
                    handler.call(instance, event, $(this))
                };
                target.bind(eventType, handlers[eventType])
            });
            key = group + "/" + (target[0] === window ? "window" : target[0][$.expando]);
            this._activeEvents[key] = handlers
        }
    },
    _unassignEvents: function (group, target) {
        var key, handlers, eventType;
        this._activeEvents = this._activeEvents || {};
        if (typeof target === "string") {
            key = group + "/" + target;
            handlers = this._activeEvents[key];
            if (!handlers) {
                return
            }
            for (eventType in handlers) {
                $(document).undelegate(target, eventType, handlers[eventType])
            }
        } else {
            target = $(target);
            if (target.length === 0) {
                return
            }
            key = group + "/" + target[0][$.expando];
            handlers = this._activeEvents[key];
            if (!handlers) {
                return
            }
            try {
                for (eventType in handlers) {
                    target.unbind(eventType, handlers[eventType])
                }
            } catch (error) {
                var events = $.data(target[0], "events");
                if (events) {
                    for (eventType in handlers) {
                        var $handlers = events[eventType];
                        if (!$handlers) {
                            continue
                        }
                        var i = $handlers.length;
                        while (i--) {
                            if ($handlers[i].handler === handlers[eventType]) {
                                $handlers.splice(i, 1);
                                if ($handlers.length === 0) {
                                    delete events[eventType]
                                }
                                break
                            }
                        }
                    }
                }
            }
        }
        delete this._activeEvents[key]
    },
    _isValidInput: function () {
        return true
    },
    _handleKeyEvent: function (e) {
        var instance = this;
        if (instance._isValidInput(e)) {
            var SpecialKey = JIRA.Keyboard.SpecialKey,
                shortcut = JIRA.Keyboard.shortcutEntered(e);
            if (shortcut) {
                if (instance.keys[shortcut]) {
                    instance.keys[shortcut].call(instance, e);
                    return
                } else {
                    if ((shortcut === SpecialKey.BACKSPACE || shortcut === SpecialKey.DELETE) && instance.keys.onEdit) {
                        instance.keys.onEdit.call(instance, e);
                        return
                    }
                }
            }
            var character = JIRA.Keyboard.characterEntered(e);
            if (character && instance.keys.onEdit) {
                instance.keys.onEdit.call(instance, e, character)
            }
        }
    },
    getCustomEventName: function (methodName) {
        return (this.CLASS_SIGNATURE || "") + "_" + methodName
    },
    _getCustomEventArgs: function () {
        return [this]
    },
    trigger: function (event) {
        return NJS.trigger(event, this)
    },
    _supportsBoxShadow: function () {
        var s = document.body.style;
        return s.WebkitBoxShadow !== undefined || s.MozBoxShadow !== undefined || s.boxShadow !== undefined
    },
    _setOptions: function (options) {
        var element, optionsFromDOM;
        options = options || {};
        if (options instanceof $ || typeof options === "string" || (typeof options === "object" && options.nodeName)) {
            options = {
                element: options
            }
        }
        element = $(options.element);
        optionsFromDOM = element.getOptionsFromAttributes();
        this.options = $.extend(true, this._getDefaultOptions(options), optionsFromDOM, options);
        if (element.length === 0) {
            return this.INVALID
        }
        return undefined
    },
    getCaret: function (node) {
        var startIndex = node.selectionStart;
        if (startIndex >= 0) {
            return (node.selectionEnd > startIndex) ? -1 : startIndex
        }
        if (document.selection) {
            var textRange1 = document.selection.createRange();
            if (textRange1.text.length === 0) {
                var textRange2 = textRange1.duplicate();
                textRange2.moveToElementText(node);
                textRange2.setEndPoint("EndToStart", textRange1);
                return textRange2.text.length
            }
        }
        return -1
    },
    _render: function () {
        var i, name = arguments[0],
            args = [];
        for (i = 1; i < arguments.length; i++) {
            args.push(arguments[i])
        }
        return this._renders[name].apply(this, args)
    }
});